#include <event.h> 
#include <stdio.h> 
#include <sys/socket.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <string.h> 
//struct event *readev = NULL; 

//处理读事件
void readcb(evutil_socket_t fd, short event, void * arg) 
{ 
     // arg 参数就是 event_new 时传入的最后一个参数
    struct event *readev = (struct event *)arg;
    
	 char buf[256] = { 0 };
	 int ret = read(fd, buf, sizeof(buf)); 
	 if (ret < 0){ 
		 perror("read err"); 
		 close(fd); 
         if(readev)
		    event_del(readev);  // 其实不需要因为文件描述符关闭后会自动清理
 	 } 
	 else if (ret == 0){ 
		 printf("client closed\n"); 
		 close(fd); 
         if (readev)
		    event_del(readev); 
	 } 
	 else{ 
        printf("recv %s\n", buf);
	 	write(fd, buf, ret);//反射
	 } 
} 
void conncb(evutil_socket_t fd, short event, void * arg) 
{ 
	 //处理连接
	 struct event_base *base = (struct event_base*)arg; 
	 struct sockaddr_in client; 
	 socklen_t len = sizeof(client); 
	 int cfd = accept(fd, (struct sockaddr*)&client, &len); 
	 if (cfd > 0){ 
		 //连接成功
		 //为每个连接创建独立的事件
		 //struct event *readev = event_new(base, cfd, EV_READ | EV_PERSIST, readcb, readev); 
		 
        struct event *readev = event_new(base, cfd, EV_READ | EV_PERSIST, readcb, NULL); 
        // 重新设置回调参数为 event 本身
        event_assign(readev, base, cfd, EV_READ | EV_PERSIST, readcb, readev);
        
         event_add(readev, NULL); 
	 } 
} 


int main() 
{ 
	 int fd = socket(AF_INET, SOCK_STREAM, 0); 
	 struct sockaddr_in serv; 
	 bzero(&serv, sizeof(serv)); 
	 serv.sin_addr.s_addr = htonl(INADDR_ANY); 
	 serv.sin_port = htons(8888); 
	 serv.sin_family = AF_INET; 
	 bind(fd, (struct sockaddr*)&serv, sizeof(serv)); 
	 listen(fd, 120); 

     // 1.创建事件基础框架事件循环event_base
	 struct event_base *base = event_base_new();
	 // 2. 创建新的事件间event， 将event绑定到event_base中
     // 监控fd上的读事件，当有客户端连接请求到达时，触发调用 conncb 回调函数，EV_PERSIST 表示事件触发后继续监听
     //struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *); 
	 struct event *connev = event_new(base, fd, EV_READ | EV_PERSIST, conncb, base); 
	 // 3. 注册并激活事件，相当于调用 epoll_ctl 函数
     event_add(connev, NULL);//开始监听
	 // 4. 循环等待
	 event_base_dispatch(base); 
	 event_base_free(base);//释放
	 event_free(connev); 
	 return 0; 
} 
